package cn.assumejson.springframework.aop.framework.autoproxy;

import cn.assumejson.springframework.aop.*;
import cn.assumejson.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl;
import cn.assumejson.springframework.aop.framework.AdvisedSupport;
import cn.assumejson.springframework.aop.framework.ProxyFactory;
import cn.assumejson.springframework.aop.framework.ProxyProcessorSupport;
import cn.assumejson.springframework.aop.framework.adapter.MethodAroundAdviceInterceptor;
import cn.assumejson.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor;
import cn.assumejson.springframework.aop.target.SingletonTargetSource;
import cn.assumejson.springframework.beans.PropertyValues;
import cn.assumejson.springframework.beans.factory.BeanFactory;
import cn.assumejson.springframework.beans.factory.BeanFactoryAware;
import cn.assumejson.springframework.beans.factory.BeansException;
import cn.assumejson.springframework.beans.factory.FactoryBean;
import cn.assumejson.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import cn.assumejson.springframework.beans.factory.support.DefaultListableBeanFactory;
import cn.assumejson.springframework.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Mr. Li
 * @version 1.0
 * @since 1.0 2021/12/2 17:34
 */
@Slf4j
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

    protected static final Object[] DO_NOT_PROXY = null;

    private BeanFactory beanFactory;

    /**
     * 早期暴露，通过该Map 进行动态代理创建
     */
    private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

    /**
     * 被代理过的Bean
     */
    public final Set<String> targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * 标记是否需要创建动态代理： 如果是aop基础bean或者需要跳过的则会标记false
     */
    private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);

    private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<>(16);

    private final Set<String> proxyMark = new HashSet<>();

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    protected abstract Object[] getAdvicesAndAdvisorsForBean(Class<?> aClass, String beanName, TargetSource customTargetSource);


    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) ||
                Pointcut.class.isAssignableFrom(beanClass) ||
                Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(beanName, bean);
        return wrapIfNecessary(bean, beanName, cacheKey);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            //获取缓存key
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            // 之前循环依赖创建的动态代理 如果是现在的bean 就不再创建，并且移除
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                // 该方法将会返回动态代理实例
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

    protected Object getCacheKey(Class<?> beanClass, String beanName) {
        if (StringUtils.hasLength(beanName)) {
            return (FactoryBean.class.isAssignableFrom(beanClass) ?
                    BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName);
        } else {
            return beanClass;
        }
    }

    private Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        log.info("动态代理对象尝试进行必要的属性赋值操作.................");
        // 标记 bean是否已经判断过的
        if (this.proxyMark.contains(beanName)) {
            return bean;
        }
        //已经被处理过（解析切面时targetSourcedBeans出现过） 就是自己实现创建动态代理逻辑
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        //不需要增强的
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        //是不是基础的bean 是不是需要跳过的 重复判断 （ 因为循环依赖是可以改变bean的，如果把bean改成了advisor呢）
        // 这里需要跳过配合动态代理的advisor
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 标记需要进行动态代理
        this.proxyMark.add(beanName);
        // 根据当前bean找到匹配的advisor
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        // 当前bean匹配到了advisor
        if (specificInterceptors != DO_NOT_PROXY) {
            // 标记为已处理
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //创建我们的真正的代理对象
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            //加入到缓存
            if (proxy != null) {
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        // 将解析过的类进行缓存
        // 生成缓存
        Object cacheKey = getCacheKey(beanClass, beanName);

        if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
            if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }

        // 代理逻辑，这里是提供用户自己实现，如果bean需要进行代理，则会跳过后续的Bean生命周期
        TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
        if (targetSource != null) {
            this.targetSourcedBeans.add(beanName);
            // 生成代理对象
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
            Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        return null;
    }

    private TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
        return null;
    }

    private TargetSource getTargetSource(Class<?> beanClass, String beanName) {

        return new SingletonTargetSource(beanClass);
    }

    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        return false;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return SmartInstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        return SmartInstantiationAwareBeanPostProcessor.super.postProcessPropertyValues(pvs, pds, bean, beanName);
    }

    private Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
        for (Object specificInterceptor : specificInterceptors) {
            PointcutAdvisor advisor = (PointcutAdvisor) specificInterceptor;
            // Assembly agent information 组装代理信息
            AdvisedSupport advisedSupport = new AdvisedSupport();

            try {
                // 创建Bean原始对象
                targetSourcedBeans.add(beanName);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 获取到所有的注解信息
            Annotation[] declaredAnnotations = ((InstantiationModelAwarePointcutAdvisorImpl) specificInterceptor).getAspectJAdviceMethod().getDeclaredAnnotations();
            MethodInterceptor methodInterceptor = null;
            for (Annotation declaredAnnotation : declaredAnnotations) {
                methodInterceptor = getMethodInterceptor(advisor, declaredAnnotation);
            }
            advisedSupport.setMethodInterceptor(methodInterceptor);
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setTargetSource(targetSource);
            Class<?>[] interfaces = beanClass.getInterfaces();
            advisedSupport.setProxyTargetClass(!(interfaces.length > 0));
            return new ProxyFactory(advisedSupport).getProxy();
        }
        return null;
    }

    protected MethodInterceptor getMethodInterceptor(PointcutAdvisor advisor, Annotation declaredAnnotation) {
        MethodInterceptor methodBeforeAdvice = null;
        InstantiationModelAwarePointcutAdvisorImpl pointcutAdvisor = (InstantiationModelAwarePointcutAdvisorImpl) advisor;
        // 执行 Aop 中的方法
        Method aspectJAdviceMethod = pointcutAdvisor.getAspectJAdviceMethod();

        String aspectName = pointcutAdvisor.getAspectName();

        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) pointcutAdvisor.getDeclaredPointcut().getBeanFactory();

        if (declaredAnnotation.annotationType().isAssignableFrom(Around.class)) {
            // 同意 解析 advisor
            methodBeforeAdvice = new MethodAroundAdviceInterceptor((method, args, target) -> aspectJAdviceMethod.invoke(beanFactory.getBean(aspectName)));
        }

        if (declaredAnnotation.annotationType().isAssignableFrom(Before.class)) {
            // 同意 解析 advisor
            methodBeforeAdvice = new MethodBeforeAdviceInterceptor((method, args, target) -> aspectJAdviceMethod.invoke(beanFactory.getBean(aspectName)));
        }

        if (declaredAnnotation.annotationType().isAssignableFrom(Around.class)) {
            // 同意 解析 advisor
            methodBeforeAdvice = new MethodAroundAdviceInterceptor((method, args, target) -> aspectJAdviceMethod.invoke(beanFactory.getBean(aspectName)));
        }

        return methodBeforeAdvice;
    }
}
